package org.ttitfly.workflow;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.ttitfly.workflow.engine.AbstractRunUnit;
import org.ttitfly.workflow.engine.Process;
import org.ttitfly.workflow.engine.ProcessFactory;
import org.ttitfly.workflow.engine.persistence.PersistenceService;
import org.ttitfly.workflow.engine.run.ProcessInstance;
import org.ttitfly.workflow.engine.run.Task;
import org.ttitfly.workflow.exception.NoSessionConnectedException;
import org.ttitfly.workflow.exception.ProcessRunException;


/**
 * 执行上下文
 * 
 * <pre>
 * 每一次工作流请求都将封装一个ExecuteContext
 * </pre>
 * 
 * @author 潘海春
 * @version 2008-12-27
 * @see Session
 */
public class ExecuteContext implements Session {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	/** 会话人 */
	private String principal;

	/** 变量数据 */
	private Map<String, Object> variables;

	/** 流程工厂 */
	private ProcessFactory processFactory;

	/** 持久化服务 */
	private PersistenceService persistenceService;

	/** 工作流连接器 */
	private SessionEstablishment sessionEstablishment;

	/** process instance */
	private ProcessInstance currentProcessInstance;

	/** task */
	private Task currentTask;

	/** 子session */
	private List<Session> childrens;
	
	/** 指示是否关闭 */
	private boolean closed = false;

	/**
	 * 构造函数
	 * 
	 * @param pricipal
	 *            请求人
	 * @param variables
	 *            请求数据
	 * @param sessionEstablishment
	 *            工作流会话管理
	 */
	public ExecuteContext(String principal, Map<String, Object> variables,
			ProcessFactory processFactory,
			PersistenceService persistenceService,
			SessionEstablishment sessionEstablishment) {
		this.principal = principal;
		this.variables = new HashMap<String, Object>();
		if (variables != null) {
			this.variables.putAll(variables);
		}
		this.processFactory = processFactory;
		this.persistenceService = persistenceService;
		this.sessionEstablishment = sessionEstablishment;
		childrens = new ArrayList<Session>();
	}

	@Override
	public String getPrincipal() {
		return principal;
	}

	@Override
	public void close() throws NoSessionConnectedException {
		if (!closed) {
			storeCurrentProcessInstance();
			storeCurrentTask();
			for (Session child : childrens) {
				// 关闭所有子session
				child.close();
			}
			sessionEstablishment.disconnect();
			cleanStates();
			closed = true;
		}
	}

	/**
	 * 清除状态
	 */
	private void cleanStates() {
		principal = null;
		variables = null;
		processFactory = null;
		persistenceService = null;
		sessionEstablishment = null;
		currentProcessInstance = null;
		currentTask = null;
		childrens = null;
	}

	@Override
	public Session fork() {
		Map<String, Object> data = new HashMap<String, Object>();
		data.putAll(getAllVariables());
		Session child = sessionEstablishment.connect(principal, data);
		child.setCurrent(currentProcessInstance);
		child.setCurrent(currentTask);
		childrens.add(child);
		return child;
	}

	@Override
	public ProcessInstance currentProcessInstance() {
		return currentProcessInstance;
	}

	@Override
	public Task currentTask() {
		return currentTask;
	}

	@Override
	public void setCurrent(ProcessInstance processInstance) {
		if (currentProcessInstance != null) {
			storeCurrentProcessInstance();
		}
		currentProcessInstance = processInstance;
		if (currentProcessInstance != null && 
				currentProcessInstance.getId() <= 0) {
			persistenceService.save(currentProcessInstance);
		}
	}

	@Override
	public void setCurrent(Task task) {
		if (currentTask != null) {
			storeCurrentTask();
		}
		currentTask = task;
		if (currentTask != null && currentTask.getId() <= 0) {
			persistenceService.save(task);
		}
	}
	
	

	@Override
	public WFRequest getRequest() {
		return this;
	}

	@Override
	public Map<String, Object> getAllVariables() {
		return variables;
	}

	@Override
	public Object getVariable(String name) {
		return variables.get(name);
	}

	@Override
	public void put(Map<String, Object> variables) {
		this.variables.putAll(variables);
	}

	@Override
	public void setVariable(String name, Object value) {
		variables.put(name, value);
	}

	@Override
	public ProcessInstance start(String processName) throws ProcessRunException {
		Process process = processFactory.getProcess(processName);
		if (process == null) {
			throw new ProcessRunException("找不到名称为[" + processName + "]的流程定义！");
		}
		process.start();
		return currentProcessInstance();
	}

	@Override
	public ProcessInstance start(String processName, String actionName)
			throws ProcessRunException {
		Process process = processFactory.getProcess(processName);
		if (process == null) {
			throw new ProcessRunException("找不到名称为[" + processName + "]的流程定义！");
		}
		process.start(actionName);
		return currentProcessInstance;
	}

	@Override
	public ProcessInstance start(Task task, String processName) throws ProcessRunException {
		Process process = processFactory.getProcess(processName);
		process.start();
		currentProcessInstance.setParent(task);
		return currentProcessInstance;
	}

	@Override
	public ProcessInstance start(Task task, String processName, String actionName) throws ProcessRunException {
		Process process = processFactory.getProcess(processName);
		process.start(actionName);
		currentProcessInstance.setParent(task);
		return currentProcessInstance;
	}

	@Override
	public void execute(int taskId) throws ProcessRunException {
		Task task = persistenceService.getTask(taskId);
		ProcessInstance pi = task.getProcessInstance();
		Process process = processFactory.getProcess(pi.getProcessName(), pi.getProcessVersion());
		setCurrent(pi);
		setCurrent(task);
		process.execute(task.getUnitName());
	}

	@Override
	public void execute(int taskId, String actionName) throws ProcessRunException {
		Task task = persistenceService.getTask(taskId);
		ProcessInstance pi = task.getProcessInstance();
		Process process = processFactory.getProcess(pi.getProcessName(), pi.getProcessVersion());
		setCurrent(pi);
		setCurrent(task);
		process.execute(task.getUnitName(), actionName);
	}

	@Override
	public int countClosedTask(String processName, String activityName, int processInstanceId) {
		if (principal == null || "".equals(principal)) {
			return 0;
		}
		return persistenceService.countTask(principal, processName, activityName, processInstanceId, new int[] {AbstractRunUnit.CLOSED});
	}

	@Override
	public int countRunningTask(String processName, String activityName, int processInstanceId) {
		if (principal == null || "".equals(principal)) {
			return 0;
		}
		return persistenceService.countTask(principal, processName, activityName, processInstanceId, new int[] {AbstractRunUnit.RUNNING});
	}

	@Override
	public List<Task> findClosedTask(String processName, String activityName, int processInstanceId, int start, int count) {
		if (principal == null || "".equals(principal)) {
			return new ArrayList<Task>();
		}
		return persistenceService.findTask(principal, processName, activityName, processInstanceId, new int[] {AbstractRunUnit.CLOSED}, start, count);
	}

	@Override
	public List<Task> findRunningTask(String processName, String activityName, int processInstanceId, int start, int count) {
		if (principal == null || "".equals(principal)) {
			return new ArrayList<Task>();
		}
		return persistenceService.findTask(principal, processName, activityName, processInstanceId, new int[] {AbstractRunUnit.RUNNING}, start, count);
	}
	
	private void storeCurrentProcessInstance() {
		if (currentProcessInstance != null) {
			if (currentProcessInstance.getId() <= 0) {
				persistenceService.save(currentProcessInstance);
			} else {
				persistenceService.update(currentProcessInstance);
			}
		}
	}
	
	private void storeCurrentTask() {
		if (currentTask != null) {
			if (currentTask.getId() <= 0) {
				persistenceService.save(currentTask);
			} else {
				persistenceService.update(currentTask);
			}
		}
	}

	@Override
	public void deleteProcessInstance(int processInstanceId) {
		persistenceService.deleteProcessInstance(processInstanceId);
	}

}
